﻿Array.prototype.contains = function(v)
{
    for (i = 0; i < this.length; i++)
    {
        if (this[i] == v) 
            return true;
    }
    return false;
} 

function Puzzle(unselectedBoxCssClass, selectedBoxCssClass, element)
{
    this.getNewPuzzleDataSynchronously = false;
    var boxes = new Array();

    for (var columnIndex = 0; columnIndex < 2; columnIndex++)
    {
        for (var rowIndex = 0; rowIndex < 2; rowIndex++)
        {
            var box = new Box(columnIndex, rowIndex, unselectedBoxCssClass, selectedBoxCssClass);
            if (boxes[columnIndex] == undefined)
                boxes[columnIndex] = new Array();
            boxes[columnIndex][rowIndex] = box;
            box.onIsSelectedChanged(boxSelectionChanged);
        }
    }

    this.getBox = function(columnIndex, rowIndex)
    {
        return boxes[columnIndex][rowIndex];
    }

    this.getAllBoxes = function()
    {
        return getAllBoxesInternal();
    }
    
    this.getSelectedBox = function()
    {
        var allBoxes = this.getAllBoxes();
        for (var i = 0; i < allBoxes.length; i++)
        {
            if (allBoxes[i].getIsSelected() == true)
                return allBoxes[i];
        }
        return null;
    }

    this.onKeyDown = function(key)
    {
        if (key == 49) // '1'
            this.changeValueInSelectedBox(1);
        else if (key == 50) // '2'
            this.changeValueInSelectedBox(2);
        else if (key == 51) // '3'
            this.changeValueInSelectedBox(3);
        else if (key == 52) // '4'
            this.changeValueInSelectedBox(4);
        else if (key == 8 || key == 46) // backspace or delete
            this.changeValueInSelectedBox(null);
    }
    
    this.changeValueInSelectedBox = function(value)
    {
        var selectedBox = this.getSelectedBox();
        if (selectedBox != null)
            selectedBox.setValue(value);
    }

    this.isPuzzleValid = function()
    {
        var foundValues = new Array();
        var allBoxes = this.getAllBoxes();
        for (var i = 0; i < allBoxes.length; i++)
        {
            var box = allBoxes[i];
            if (box.valueIsValid(box.getValue()) == false)
                return false;
            if (box.getValue() == null)
                return false;
            if (foundValues.contains(box.getValue()))
                return false;
            foundValues.push(box.getValue());
        }
        if (foundValues.length != 4)
            return false;
        return true;
    }

    this.validate = function()
    {
        if (this.isPuzzleValid())
            this.showAlert('The puzzle is valid!');
        else
            this.showAlert('The puzzle is invalid.');
    }

    this.showAlert = function(message)
    {
        alert(message);
    }

    this.loadNewPuzzle = function()
    {
        this.getNewPuzzleData();
    }

    this.getNewPuzzleData = function()
    {
        var puzzle = this;
        $.ajax(
        {
            type: "POST",
            contentType: "application/json; charset=utf-8",
            url: "NewPuzzleService.asmx/GetNewPuzzleData",
            async: (this.getNewPuzzleDataSynchronously != true),
            dataType: "json",
            data: "{}",
            success: function(msg)
            {
                puzzle.loadNewPuzzleData(msg.d);
            }
        });
    }

    this.loadNewPuzzleData = function(data)
    {
        var allBoxes = this.getAllBoxes();
        for (var i = 0; i < allBoxes.length; i++)
            allBoxes[i].setValue(null);

        for (var i = 0; i < data.length; i++)
        {
            var box = this.getBox(data[i][0], data[i][1]);
            box.setValue(data[i][2]);
        }
    }
    
    function getAllBoxesInternal()
    {
        var list = new Array();
        for (var column = 0; column < boxes.length; column++)
        {
            for (var row = 0; row < boxes[column].length; row++)
                list.push(boxes[column][row]);
        }
        return list;
    }

    function boxSelectionChanged(box)
    {
        if (box.getIsSelected() == true)
        {
            var allBoxes = getAllBoxesInternal();
            for (var i = 0; i < allBoxes.length; i++)
            {
                if (allBoxes[i] != box)
                    allBoxes[i].clearSelection();
            }
        }
    }
}

function Box(columnIndex, rowIndex, unselectedBoxCssClass, selectedBoxCssClass)
{
    var value = null;
    var isSelected = false;
    var element = null;
    var onIsSelectedChangedListeners = new Array();

    this.getColumnIndex = function()
    {
        return columnIndex;
    }

    this.getRowIndex = function()
    {
        return rowIndex;
    }

    this.getValue = function()
    {
        return value;
    }

    this.setValue = function(newValue)
    {
        if (this.valueIsValid(newValue) == true)
        {
            value = newValue;
            if (element != null)
            {
                if (value != null)
                    element.innerHTML = value;
                else
                    element.innerHTML = '';
            }
        }
        else
            throw ('Invalid value.');
    }

    this.getIsSelected = function()
    {
        return isSelected;
    }

    this.setIsSelected = function(newValue)
    {
        if (isSelected != newValue)
        {
            isSelected = newValue;
            afterIsSelectedChanged(this);
        }
    }

    this.getElement = function()
    {
        return element;
    }

    this.setElement = function(newValue)
    {
        if (element != null)
            element.box = undefined;
        element = newValue;
        if (element != null)
            element.box = this;
        setCssClass();
    }

    this.toggleSelection = function()
    {
        isSelected = !isSelected;
        afterIsSelectedChanged(this);
    }

    this.clearSelection = function()
    {
        isSelected = false;
        afterIsSelectedChanged(this);
    }

    this.valueIsValid = function(value)
    {
        return (value == null || (value >= 1 && value <= 4));
    }

    this.onIsSelectedChanged = function(fn)
    {
        onIsSelectedChangedListeners.push(fn);
    }

    function afterIsSelectedChanged(box)
    {
        setCssClass();

        if (onIsSelectedChangedListeners.length > 0)
        {
            for (var i = 0; i < onIsSelectedChangedListeners.length; i++)
                onIsSelectedChangedListeners[i](box);
        }
    }
    
    function setCssClass()
    {
        element.className = (isSelected ? selectedBoxCssClass : unselectedBoxCssClass);
    }
}
